function objectTest() {
    var person = new Object();
    person.name = "Nicholas";
    person.age = 29;
    person.job = "Software Engineer";
    person.sayName = function () {
        alert(this.name);
    };
    var person2 = {
        name: "Nicholas",
        age: 29,
        job: "Software Engineer",
        sayName: function () {
            alert(this.name);
        }
    };

    var person = {};
    Object.defineProperty(person, "name", {
        writable: false,
        value: "Nicholas"
    });
    alert(person.name); //"Nicholas"
    person.name = "Greg";
    alert(person.name); //"Nicholas"

    var person = {};
    Object.defineProperty(person, "name", {
        configurable: false,
        value: "Nicholas"
    });
    alert(person.name); //"Nicholas"
    delete person.name;
    alert(person.name); //"Nicholas"
}

function objectTest2() {
    var book = {
        _year: 2004,
        edition: 1
    };
    Object.defineProperty(book, "year", {
        get: function () {
            return this._year;
        },
        set: function (newValue) {
            if (newValue > 2004) {
                this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    });
    book.year = 2005;
    alert(book.edition); //2

    var book = {
        _year: 2004,
        edition: 1
    };
//定义访问器的旧有方法
    book.__defineGetter__("year", function () {
        return this._year;
    });
    book.__defineSetter__("year", function (newValue) {
        if (newValue > 2004) {
            this._year = newValue;
            this.edition += newValue - 2004;
        }
    });
    book.year = 2005;
    alert(book.edition); //2
}

function objectTest3() {
    var book = {};
    Object.defineProperties(book, {
        _year: {
            value: 2004
        },
        edition: {
            value: 1
        },
        year: {
            get: function () {
                return this._year;
            },
            set: function (newValue) {
                if (newValue > 2004) {
                    this._year = newValue;
                    this.edition += newValue - 2004;
                }
            }
        }
    });
    var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
    alert(descriptor.value); //2004
    alert(descriptor.configurable); //false
    alert(typeof descriptor.get); //"undefined"
    var descriptor = Object.getOwnPropertyDescriptor(book, "year");
    alert(descriptor.value); //undefined
    alert(descriptor.enumerable); //false
    alert(typeof descriptor.get); //"function"
}

// 工厂模式
function objectTest4() {
    function createPerson(name, age, job) {
        var o = {};
        o.name = name;
        o.age = age;
        o.job = job;
        o.sayName = function () {
            alert(this.name);
        };
        return o;
    }

    var person1 = createPerson("Nicholas", 29, "Software Engineer");
    var person2 = createPerson("Greg", 27, "Doctor");
    alert(person1.sayName());
    alert(person2.sayName());
}

//构造函数模式
function objectTest5() {
    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = function () {
            alert(this.name);
        };
    }

    var person1 = new Person("Nicholas", 29, "Software Engineer");
    var person2 = new Person("Greg", 27, "Doctor");
    alert(person1.constructor === Person); //true
    alert(person2.constructor === Person); //true

    // 当作构造函数使用
    var person = new Person("Nicholas", 29, "Software Engineer");
    person.sayName(); //"Nicholas"
    // 作为普通函数调用
    Person("Greg", 27, "Doctor");
    window.sayName(); //"Greg"
    // 在另一个对象的作用域中调用
    var o = {};
    Person.call(o, "Kristen", 25, "Nurse");
    o.sayName(); //"Kristen"


    //优化了对象中的方法多次创建的问题，但是在全局作用域中定义的函数实际上只
    // 能被某个对象调用，这让全局作用域有点名不副实。而更让人无法接受的是：如果对象需要定义很多方
    // 法，那么就要定义很多个全局函数，于是我们这个自定义的引用类型就丝毫没有封装性可言了。
    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = sayName;
    }

    function sayName() {
        alert(this.name);
    }

    var person1 = new Person("Nicholas", 29, "Software Engineer");
    var person2 = new Person("Greg", 27, "Doctor");
}

//原型模式
function objectTest6() {
    function Person() {
    }

    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "Software Engineer";
    Person.prototype.sayName = function () {
        alert(this.name);
    };
    var person1 = new Person();
    person1.sayName(); //"Nicholas"
    var person2 = new Person();
    person2.sayName(); //"Nicholas"
    alert(person1.sayName === person2.sayName); //true

    alert(Person.prototype.isPrototypeOf(person1)); //true
    alert(Person.prototype.isPrototypeOf(person2)); //true
    alert(Object.getPrototypeOf(person1) === Person.prototype); //true
    alert(Object.getPrototypeOf(person1).name); //"Nicholas"
}

function objectTest7() {
    function Person() {
    }

    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "Software Engineer";
    Person.prototype.sayName = function () {
        alert(this.name);
    };
    var person1 = new Person();
    var person2 = new Person();
    // alert(person1.hasOwnProperty("name")); //false
    // alert("name" in person1); //true
    person1.name = "Greg";
    alert(person1.name); //"Greg"——来自实例
    alert(person2.name); //"Nicholas"——来自原型
    // alert(person1.hasOwnProperty("name")); //true
    // alert("name" in person1); //true
    delete person1.name;
    // alert(person2.hasOwnProperty("name")); //false
    // alert("name" in person1); //true
    alert(person1.name); //"Nicholas"——来自原型
}

// 由于in 操作符只要通过对象能够访问到属性就返回true，hasOwnProperty()只在属性存在于
// 实例中时才返回true，因此只要in 操作符返回true 而hasOwnProperty()返回false，就可以确
// 定属性是原型中的属性。
function hasPrototypeProperty(object, name) {
    return !object.hasOwnProperty(name) && (name in object);
}

function hasPrototypeTest() {
    function Person() {
    }

    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "Software Engineer";
    Person.prototype.sayName = function () {
        alert(this.name);
    };
    //重设构造函数，只适用于ECMAScript 5 兼容的浏览器
    Object.defineProperty(Person.prototype, "constructor", {
        enumerable: false,
        value: Person
    });
    var person = new Person();
    alert(hasPrototypeProperty(person, "name")); //true
    person.name = "Greg";
    alert(hasPrototypeProperty(person, "name")); //false
}

function keyTest() {
    function Person() {
    }

    Person.prototype = {
        name: "Nicholas",
        age: 29,
        job: "Software Engineer",
        sayName: function () {
            alert(this.name);
        }
    };
    var keys = Object.keys(Person.prototype);
    alert(keys); //"name,age,job,sayName"
    var p1 = new Person();
    p1.name = "Rob";
    p1.age = 31;
    var p1keys = Object.keys(p1);
    alert(p1keys); //"name,age"

    var keys = Object.getOwnPropertyNames(Person.prototype);
    alert(keys); //"constructor,name,age,job,sayName"
}

//组合使用构造函数模式和原型模式
function protoTest() {
    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ["Shelby", "Court"];
    }

    Person.prototype = {
        constructor: Person,
        sayName: function () {
            alert(this.name);
        }
    };
    var person1 = new Person("Nicholas", 29, "Software Engineer");
    var person2 = new Person("Greg", 27, "Doctor");
    person1.friends.push("Van");
    alert(person1.friends); //"Shelby,Count,Van"
    alert(person2.friends); //"Shelby,Count"
    alert(person1.friends === person2.friends); //false
    alert(person1.sayName === person2.sayName); //true
}

//动态原型模式
function protoTest2() {
    function Person(name, age, job) {
        //属性
        this.name = name;
        this.age = age;
        this.job = job;
        //方法
        if (typeof this.sayName !== "function") {
            Person.prototype.sayName = function () {
                alert(this.name);
            };
        }
    }

    var friend = new Person("Nicholas", 29, "Software Engineer");
    friend.sayName();
}

function protoTest3() {
    function SuperType() {
        this.property = true;
    }

    SuperType.prototype.getSuperValue = function () {
        return this.property;
    };

    function SubType() {
        this.subproperty = false;
    }

    //继承了SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function () {
        return this.subproperty;
    };
    var instance = new SubType();
    console.log(instance);
    alert(instance.getSuperValue()); //true
}

//组合继承
function protoTest4() {
    function SuperType(name) {
        this.name = name;
        this.colors = ["red", "blue", "green"];
    }

    SuperType.prototype.sayName = function () {
        alert(this.name);
    };

    function SubType(name, age) {
        //继承属性
        SuperType.call(this, name);
        this.age = age;
    }

    //继承方法
    SubType.prototype = new SuperType();
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function () {
        alert(this.age);
    };
    var instance1 = new SubType("Nicholas", 29);
    console.log(instance1);
    instance1.colors.push("black");
    alert(instance1.colors); //"red,blue,green,black"
    instance1.sayName(); //"Nicholas";
    instance1.sayAge(); //29
    var instance2 = new SubType("Greg", 27);
    alert(instance2.colors); //"red,blue,green"
    instance2.sayName(); //"Greg";
    instance2.sayAge(); //27
}

protoTest4();

function object(o) {
    function F() {
    }

    F.prototype = o;
    return new F();
}

(function () {
//私有变量和私有函数
    var privateVariable = 10;

    function privateFunction() {
        return false;
    }

//构造函数
    MyObject = function () {
    };
//公有/特权方法
    MyObject.prototype.publicMethod = function () {
        privateVariable++;
        return privateFunction();
    };
})();
